﻿using System;
using System.Collections.Generic;
using System.Text;
using PacMan3D.Utils;
using Tao.OpenGl;
using Tao.FreeGlut;
using PacMan3D.Objects.Models;

namespace PacMan3D.Objects
{
    public class Ghost : IRenderable, ICollidable
    {        
        public Point3D Position { get; private set; }
        private Point3D LastPosition;

        private float GSize;
        private float FloorSize;
        private float RadiusSize;

        private DirectionType DirectionType;
        public Vector Direction { get; private set; }

        private float Speed;

        private int Slices = 25;
        private int Stacks = 25;

        /// 
        /// Ghost Material 
        /// 
        private float[] Specular = { 1.0f, 1.0f, 1.0f, 1.0f };
        private int Shininess = 90;


        public int GhostNumber { get; private set; }

        /// 
        /// Ghost OpenGL Model
        /// 
        private GhostModel GhostModel;

        private float GhostAlpha;
        private float AnimeRotationAngle;

        public Ghost(int ghostNumber, Point3D position, float radius)
        {
            this.GhostAlpha = 0.9f;
            this.GhostNumber = ghostNumber;

            this.Position = position;
            this.LastPosition = position.Clone();

            this.FloorSize = radius;
            this.GSize = this.FloorSize / 2.0f;
            this.RadiusSize = this.GSize / 2.0f;
            this.Speed = this.RadiusSize / 4.0f;

            this.Direction = new Vector(-1.0f, 0.0f, 0.0f);

            ///
            /// Ghost OpenGL Model
            /// 
            this.GhostModel = new GhostModel(this.RadiusSize, this.Slices, this.Stacks, RGBAColor.GetPalleteColorByIndex(this.GhostNumber, this.GhostAlpha));

            WallColision();
        }

        public override void DrawObject()
        {
            Gl.glEnable(Gl.GL_LIGHTING);
            Gl.glPushMatrix();
                // para o trazer a vida para cima do chão
                Gl.glTranslatef(0, this.RadiusSize, 0);

                // para deslocar o fantasma
                Gl.glTranslatef(this.Position.X, this.Position.Y, this.Position.Z);

                // para colocar o fantasma na sua direção 
                Gl.glRotatef((float)this.DirectionType, 0.0f, 1.0f, 0.0f);

                // para a animação do fantasma
                Gl.glRotatef(this.AnimeRotationAngle, 0.0f, 1.0f, 0.0f);

                // define as propriedades de reflectância do material
                Gl.glMaterialfv(Gl.GL_FRONT, Gl.GL_SPECULAR, this.Specular);

                // define as propriedades de brilho do material
                Gl.glMateriali(Gl.GL_FRONT, Gl.GL_SHININESS, this.Shininess);
                
                this.GhostModel.DrawObject();
            Gl.glPopMatrix();
            Gl.glDisable(Gl.GL_LIGHTING);
        }

        private void DrawDirectionVetor()
        {
            Gl.glColor4f(1, 1, 1, 1);
            Gl.glPushMatrix();
            Gl.glTranslatef(this.Position.X, this.RadiusSize, this.Position.Z);
            Gl.glRotatef(this.AnimeRotationAngle, 0.0f, 1.0f, 0.0f);
            Gl.glBegin(Gl.GL_LINES);
            Gl.glVertex3f(0.0f, 0.0f, 0.0f);
            Gl.glVertex3f(2.0f * this.Direction.X, 0.0f, 2.0f * this.Direction.Z);
            Gl.glEnd();
            Gl.glPopMatrix();
        }


        #region Anime Ghost Methods
        public void PlayEatPacMacAnime()
        {
            this.AnimeRotationAngle += 10.0f;
        }

        public void StopEatPacMacAnime()
        {
            this.AnimeRotationAngle = 0.0f;
        }
        #endregion


        #region Collision Handler Methods
        private int WallCollisionCounter = 0;
        public void WallColision()
        {
            this.Position = this.LastPosition;

            this.WallCollisionCounter++;

            ///
            /// rotate 90º, 180º, 270º (randomly)
            ///
            float incAngle = 90.0f * RandomNumber.Instance().NextInt(1, 4);
            UpdateDirection(incAngle);
        }

        public void PacManCollision()
        {
            this.Position = this.LastPosition;

            ///
            /// rotate 180º
            ///
            UpdateDirection(180.0f);
        }

        public void GhostCollision()
        {
            this.Position = this.LastPosition;

            ///
            /// rotate 180º
            ///
            UpdateDirection(180.0f);
        }

        public void EmptyBoxCollision()
        {
            if (this.WallCollisionCounter <= 2)
                return;

            this.WallCollisionCounter = 0;

            ///
            /// rotate 90º, 270º (randomly)
            ///
            float incAngle = 90.0f + 180.0f * RandomNumber.Instance().NextInt(0, 2);
            UpdateDirection(incAngle);
        }

        private void UpdateDirection(float incAngle)
        {
            this.DirectionType = (DirectionType)((((float)this.DirectionType) + incAngle) % 360.0f);

            switch (this.DirectionType)
            {
                case DirectionType.Left:
                    this.Direction.UpdateOnlyX(-1.0f);
                    break;

                case DirectionType.Down:
                    this.Direction.UpdateOnlyZ(1.0f);
                    break;

                case DirectionType.Up:
                    this.Direction.UpdateOnlyZ(-1.0f);
                    break;

                case DirectionType.Right:
                    this.Direction.UpdateOnlyX(1.0f);
                    break;
            }
        }
        #endregion


        public void UpdatePosition()
        {
            this.LastPosition = this.Position.Clone();
            this.Position.X += this.Direction.X * this.Speed;
            this.Position.Z += this.Direction.Z * this.Speed;
        }


        #region Methods to Get Position Ahead, ALeft, ARight
        public Point3D GetPositionAhead()
        {
            return new Point3D((float)(this.Position.X + this.Direction.X * this.Size), 0.0f,
                ((float)this.Position.Z + this.Direction.Z * this.Size));
        }

        public Point3D GetPositionALeft()
        {
            Vector newDirection = this.Direction.Rotate270XZ();
            return new Point3D((float)(this.Position.X + newDirection.X * this.FloorSize), 0.0f,
                ((float)this.Position.Z + newDirection.Z * this.FloorSize));
        }

        public Point3D GetPositionARight()
        {
            Vector newDirection = this.Direction.Rotate90XZ();
            return new Point3D((float)(this.Position.X + newDirection.X * this.FloorSize), 0.0f,
                ((float)this.Position.Z + newDirection.Z * this.FloorSize));
        }
        #endregion


        #region ICollidable Members

        Point3D ICollidable.Position
        {
            get { return this.Position; }
        }

        /// <summary>
        /// NAO RETIRAR: 2 * this.Lenght
        /// </summary>
        public float Size
        {
            get { return this.GSize; }
        }

        public bool Collides(ICollidable obj)
        {
            return false;   
        }

        public bool CollidesWithPacMan(PacMan obj)
        {
            return (Math.Abs(this.Position.X - obj.Position.X) < (this.RadiusSize + obj.Size)
                && Math.Abs(this.Position.Z - obj.Position.Z) < (this.RadiusSize + obj.Size));
        }

        public bool CollidesWithWall(WallBox obj)
        {
            return (Math.Abs(this.Position.X - obj.Position.X) < (this.Size + obj.Size)
                && Math.Abs(this.Position.Z - obj.Position.Z) < (this.Size + obj.Size));
        }

        public bool CollidesWithOtherGhost(Ghost obj)
        {
            return (Math.Abs(this.Position.X - obj.Position.X) < (this.RadiusSize + obj.RadiusSize)
                && Math.Abs(this.Position.Z - obj.Position.Z) < (this.RadiusSize + obj.RadiusSize));
        }

        #endregion

    }
}
